import datetime,math
from jili.price.ta import creat_ta_calcor as ta_frame
from jili.data.obj import gettradetime_byobj
from jili.data.db import tradecal
from copy import deepcopy
#---------------k线字段
#open,high,low,close,volume
#vwap:均价，pos:剩余仓位，updatetime
#使用dict；考虑场景：
""":param
美国纽交所商品市场
1.美国时间 自然日前一天 18点到当日17点，中间没有休盘  实际是有电子盘有场内盘
2.输入支持日历使用美国交易日历，北京时间   输出便于处理  为北京时间  k1d  为
 pre 18~17   
 6~next 5
3.考虑使用北京时间tick与美国时间tick
"""
import datetime
import pytz

def usatime2beijing(old_dt):
    """
    将任一时区的时间转换成指定时区的时间
    如果没有指定目的时区，则默认转换成当地时区

    :param time_str:
    :param src_timezone: 要转换的源时区，如"Asia/Shanghai"， "UTC"
    :param dst_timezone: 要转换的目的时区，如"Asia/Shanghai", 如果没有指定目的时区，则默认转换成当地时区
    :param dst_timezone: 时间格式
    :return: str, 字符串时间格式
    """
    # 将源时区的datetime形式转换成GMT时区(UTC+0)的datetime形式
    us = pytz.country_timezones('us')
    src_timezone=us[0]
    dt = pytz.timezone(src_timezone).localize(old_dt)
    utc_dt = pytz.utc.normalize(dt.astimezone(pytz.utc))

    # 将GMT时区的datetime形式转换成指定的目的时区的datetime形式
    # if dst_timezone:
    #     _timezone = pytz.timezone(dst_timezone)
    #     new_dt = _timezone.normalize(utc_dt.astimezone(_timezone))
    # else:
        # 未指定目的时间，默认转换成当地时区
    new_dt = utc_dt.astimezone()
    # 转换成字符串时间格式
    return new_dt.replace(tzinfo=None)
def copydict(t):
    r={}
    for k,v in t.items():
        r[k]=v
    return r
class kline1d:
    def __init__(self,obj="",isnight=True,bars_len=2,isusatime=False):
        from jili.data.db import tradecal
        self.isnight=isnight
        self.obj=obj
        self.bar={}
        self.bars=[]
        self.tradecal=tradecal
        self.lasttimekey=None
        self.bars_len=bars_len
        self.tas = []
        self.onbarcall=[]
        self.isusatime=isusatime
    def setonbar(self,call):
        self.onbarcall.append(call)
    def add_ta(self, ta_arg):
        ta0 = ta_frame(ta_arg)
        self.tas.append(ta0)
    def __calc_ta(self,bar):
        for i in self.tas:
            r = i.onbar(bar)
            bar.update(r)
    def gethisbars(self,date=None,num=60):
        if date is None:
            date0=datetime.datetime.now()
        elif isinstance(date,str):
            from jili.core.convert import str2datetime
            date0=str2datetime(date)
        else:
            date0=date
    def ontick(self,t):
        timekey=t["timekey"]
        if self.bars:
            bar0=self.bars[-1]
            bartimekey=self.gettimekey(timekey)
            if bartimekey == bar0["timekey"]:
                if "open" in t.keys():
                    if bar0["high"] != t["high"]:
                        bar0["high"] = t["high"]
                        bar0["hightime"] = timekey
                    elif bar0["low"] != t["low"]:
                        bar0["low"] = t["low"]
                        bar0["lowtime"] = timekey
                else:
                    if bar0["high"] < t["LastPrice"]:
                        bar0["high"] = t["LastPrice"]
                        bar0["hightime"] = timekey
                    elif bar0["low"] > t["LastPrice"]:
                        bar0["low"] = t["LastPrice"]
                        bar0["lowtime"] = timekey
                bar0["close"] = t["LastPrice"]
                bar0["volume"] = t["Volume"]
                if "AveragePrice" in t.keys():
                    bar0["vwap"] = t["AveragePrice"]
                else:
                    bar0["vwap"] = 0
                bar0["pos"]=t["pos"]
                bar0["uptime"] = timekey
                self.__calc_ta(bar0)
            else:
                bar0 = {}
                bar0["timekey"] = bartimekey
                bar0["obj"] = t["obj"]
                if "open" in t.keys():
                    bar0["open"] = t["open"]
                    bar0["high"] = t["high"]
                    bar0["low"] = t["low"]
                else:
                    bar0["open"] = t["LastPrice"]
                    bar0["high"] = t["LastPrice"]
                    bar0["low"] = t["LastPrice"]
                bar0["close"] = t["LastPrice"]
                bar0["volume"] = t["Volume"]
                if "AveragePrice" in t.keys():
                    bar0["vwap"] = t["AveragePrice"]
                else:
                    bar0["vwap"] = 0
                bar0["pos"] = t["pos"]
                bar0["uptime"] = timekey
                bar0["hightime"] = timekey
                bar0["lowtime"] = timekey
                bar0["opentime"] = timekey
                self.__calc_ta(bar0)
                self.bars.append(bar0)
                self.calldayend(timekey,bar0)
                if len(self.bars)>self.bars_len:
                    self.bars.pop(0)
                self.bar = bar0
        else:
            bar0={}
            bar0["timekey"] = self.gettimekey(timekey)
            bar0["obj"] = t["obj"]
            if "open" in t.keys():
                bar0["open"] = t["open"]
                bar0["high"] = t["high"]
                bar0["low"] = t["low"]
            else:
                bar0["open"] = t["LastPrice"]
                bar0["high"] = t["LastPrice"]
                bar0["low"] = t["LastPrice"]
            bar0["close"] = t["LastPrice"]
            bar0["volume"] = t["Volume"]
            if "AveragePrice" in t.keys():
                bar0["vwap"] = t["AveragePrice"]
            else:
                bar0["vwap"] = 0
            bar0["pos"]=t["pos"]
            bar0["uptime"] = timekey
            bar0["hightime"] = timekey
            bar0["lowtime"] = timekey
            bar0["opentime"] = timekey
            self.__calc_ta(bar0)
            self.bars.append(bar0)
            self.calldayend(timekey, bar0)
            self.bar=bar0
        return bar0
    def calldayend(self,timekey,bar0):
        if self.isusatime:
            if timekey.hour==16 and timekey.minute>=59:
                for i in self.onbarcall:
                    i(bar0)
            else:
                return False
        else:
            if timekey.hour==4 and timekey.minute>=59:
                for i in self.onbarcall:
                    i(bar0)
            else:
                return False
    def gettimekey(self,a):
        hour = a.hour
        if self.isusatime:
            if hour >= 18:
                return self.tradecal.getnexttradeday(a,region="A")
            else:
                return datetime.datetime(a.year, a.month, a.day)
        else:
            if hour < 6:
                return a-datetime.timedelta(days=1)
            else:
                return datetime.datetime(a.year, a.month, a.day)
    def onbar(self,b):
        timekey = self.gettimekey(b["uptime"])
        if self.bars:
            bar0=self.bars[-1]
            if timekey == bar0["timekey"]:
                if bar0["high"] < b["high"]:
                    bar0["high"] = b["high"]
                    bar0["hightime"] = b["hightime"]
                elif bar0["low"] > b["low"]:
                    bar0["low"] =b["low"]
                    bar0["lowtime"] = b["lowtime"]
                bar0["close"] = b["close"]
                bar0["volume"] = bar0["volume"]+b["volume"]
                bar0["vwap"] = b["vwap"]
                bar0["pos"]=b["pos"]
                bar0["uptime"] = b["uptime"]
                self.__calc_ta(bar0)
            else:
                bar0 = copydict(b)
                bar0["timekey"] = timekey
                self.__calc_ta(bar0)
                self.bars.append(bar0)
                self.calldayend(b["uptime"], bar0)
                if len(self.bars)>self.bars_len:
                    self.bars.pop(0)
                self.bar = bar0
        else:
            bar0=copydict(b)
            bar0["timekey"] = timekey
            self.__calc_ta(bar0)
            self.bars.append(bar0)
            self.calldayend(b["uptime"], bar0)
            self.bar=bar0
        return bar0
class kline1m:
    def __init__(self,obj,bars_len=2,ktype="1110",isusatime=False):
        self.oncallkey=None
        self.obj=obj
        self.iscall=True
        self.bars_len=bars_len
        self.pretimekey=None
        self.bar={}
        self.bars = []
        self.onbarcall = []
        self.tas=[]
        self.ktype=ktype
        self.isusatime=isusatime
        """:cvar
        0位:是否过滤无成交数据量：1：过滤，0：不过滤
        1位：k触发回调规则：0：滞后触发，1.提前触发，2.实时触发
        2位：是否补齐k线；1：补齐，0：不补
        3位：是否整点tick共用k线，1：是，0：否
        1110：
        1100
        0110
        0100
        0011
        0001
        """
    def add_ta(self,ta_arg):
        ta0=ta_frame(ta_arg)
        self.tas.append(ta0)
    def __calc_ta(self,bar):
        #print(bar)
        for i in self.tas:
            r = i.onbar(bar)
            #print(r)
            bar.update(r)
        return bar
    def reset(self):
        self.bar={}
        self.bars = []
    def setonbar(self,call):
        self.onbarcall.append(call)
    def ontick(self,t):
        """

        Args:
            t: ["timekey","LastPrice","volume","AveragePrice","OpenInterest"]

        Returns:

        """
        #print("kline1m",t)
        timekey0=t["timekey"]
        vv="Volume"
        if self.ktype[0]=="1":
            vv="volume"
        if t[vv] == 0:
            return {}
        if self.checktime(timekey0):
            timekey=self.getbartimekey(timekey0)
            if self.bars:
                bar=self.bars[-1]
                if timekey==bar["timekey"]:
                    if bar["high"]<t["LastPrice"]:
                        bar["high"] = t["LastPrice"]
                        bar["hightime"] = timekey0
                    elif bar["low"] > t["LastPrice"]:
                        bar["low"] = t["LastPrice"]
                        bar["lowtime"] = timekey0
                    bar["close"]=t["LastPrice"]
                    bar["volume"]=bar["volume"]+t["volume"]
                    if "AveragePrice" in t.keys():
                        bar["vwap"]=t["AveragePrice"]
                    else:
                        bar["vwap"] =0
                    bar["pos"]=t["pos"]
                    bar["uptime"]=timekey0
                    if self.ktype[1] == "1":
                        if timekey0.second >= 59:
                            if self.iscall:
                                self.__calc_ta(bar)
                                if self.onbarcall:
                                    for i in self.onbarcall:
                                        i(self.bar)
                                self.iscall = False
                        else:
                            self.iscall = True
                    elif self.ktype[1] == "2":
                        if self.onbarcall:
                            self.__calc_ta(bar)
                            for i in self.onbarcall:
                                i(self.bar)
                else:
                    if self.iscall and self.ktype[1] == "1":
                        self.__calc_ta(bar)
                        if self.onbarcall:
                            for i in self.onbarcall:
                                i(bar)
                    if self.ktype[3]=="1":
                        if timekey0 == timekey:
                            if bar["high"] < t["LastPrice"]:
                                bar["high"] = t["LastPrice"]
                                bar["hightime"] = timekey0
                            elif bar["low"] > t["LastPrice"]:
                                bar["low"] = t["LastPrice"]
                                bar["lowtime"] = timekey0
                            bar["close"] = t["LastPrice"]
                            bar["volume"] = bar["volume"] + t["volume"]
                            if "AveragePrice" in t.keys():
                                bar["vwap"] = t["AveragePrice"]
                            else:
                                bar["vwap"] = 0
                            bar["pos"] = t["pos"]
                            bar["uptime"] = timekey0
                            if self.ktype[1] != "1":
                                self.__calc_ta(bar)
                                if self.onbarcall:
                                    for i in self.onbarcall:
                                        i(bar)
                            bar0 = {}
                            bar0["timekey"] = timekey
                            bar0["obj"] = self.obj
                            bar0["open"] = t["LastPrice"]
                            bar0["high"] = t["LastPrice"]
                            bar0["low"] = t["LastPrice"]
                            bar0["close"] = t["LastPrice"]
                            bar0["volume"] = 0
                            if "AveragePrice" in t.keys():
                                bar0["vwap"] = t["AveragePrice"]
                            else:
                                bar0["vwap"] = 0
                            bar0["pos"] = t["pos"]
                            bar0["uptime"] = timekey0
                            bar0["hightime"] = timekey0
                            bar0["lowtime"] = timekey0
                            bar0["opentime"] = timekey0
                            self.bars.append(bar0)
                            if len(self.bars) > self.bars_len:
                                self.bars.pop(0)
                            self.bar = bar0
                        else:
                            if self.ktype[1] != "1":
                                self.__calc_ta(self.bar)
                                if self.onbarcall:
                                    for i in self.onbarcall:
                                        i(self.bar)
                            bar0 = {}
                            bar0["timekey"] = timekey
                            bar0["obj"] = self.obj
                            bar0["open"] = t["LastPrice"]
                            bar0["high"] = t["LastPrice"]
                            bar0["low"] = t["LastPrice"]
                            bar0["close"] = t["LastPrice"]
                            bar0["volume"] = t["volume"]
                            if "AveragePrice" in t.keys():
                                bar0["vwap"] = t["AveragePrice"]
                            else:
                                bar0["vwap"] = 0
                            bar0["pos"] = t["pos"]
                            bar0["uptime"] = timekey0
                            bar0["hightime"] = timekey0
                            bar0["lowtime"] = timekey0
                            bar0["opentime"] = timekey0
                            self.bars.append(bar0)
                            if len(self.bars) > self.bars_len:
                                self.bars.pop(0)
                            self.bar = bar0
                        if self.ktype[1] == "2":
                            self.__calc_ta(bar0)
                            if self.onbarcall:
                                for i in self.onbarcall:
                                    i(self.bar)
                    else:
                        bar0={}
                        bar0["timekey"]=timekey
                        bar0["obj"]=self.obj
                        bar0["open"]=t["LastPrice"]
                        bar0["high"]=t["LastPrice"]
                        bar0["low"]=t["LastPrice"]
                        bar0["close"]=t["LastPrice"]
                        bar0["volume"]=t["volume"]
                        if "AveragePrice" in t.keys():
                            bar0["vwap"] = t["AveragePrice"]
                        else:
                            bar0["vwap"] = 0
                        bar0["pos"]=t["pos"]
                        bar0["uptime"]=timekey0
                        bar0["hightime"] = timekey0
                        bar0["lowtime"] = timekey0
                        bar0["opentime"] = timekey0
                        self.bars.append(bar0)
                        if len(self.bars)>self.bars_len:
                            self.bars.pop(0)
                        self.bar=bar0
                        if self.ktype[1] == "2":
                            self.__calc_ta(bar0)
                            if self.onbarcall:
                                for i in self.onbarcall:
                                    i(self.bar)
                    self.iscall = True
            else:
                bar0={}
                bar0["timekey"]=timekey
                bar0["obj"]=self.obj
                bar0["open"]=t["LastPrice"]
                bar0["high"]=t["LastPrice"]
                bar0["low"]=t["LastPrice"]
                bar0["close"]=t["LastPrice"]
                bar0["volume"]=t["volume"]
                if "AveragePrice" in t.keys():
                    bar0["vwap"] = t["AveragePrice"]
                else:
                    bar0["vwap"] = 0
                bar0["pos"]=t["pos"]
                bar0["uptime"]=timekey0
                bar0["hightime"] = timekey0
                bar0["lowtime"] = timekey0
                bar0["opentime"] = timekey0
                self.bars.append(bar0)
                self.bar=bar0
                if self.ktype[1] == "2":
                    self.__calc_ta(bar0)
                    if self.onbarcall:
                        for i in self.onbarcall:
                            i(self.bar)
                self.iscall = True
            self.pretimekey=timekey
        return self.bar
    def getbartimekey(self,a):
        if self.isusatime:
            a=usatime2beijing(a)
        b = datetime.datetime(a.year, a.month, a.day, a.hour, a.minute)
        return b
    def checktime(self,a):
        return True
def subtime_minute(t1,t0):
    return (t1.hour-t0.hour)*60+t1.minute-t0.minute
def addtime_mintue(t0,m):
    m=m+t0.minute
    return [t0.hour+m//60,m%60]
def time2datetime(t):
    return datetime.datetime(year=2000,month=1,day=1,hour=t.hour,minute=t.minute,second=t.second)
class klinenm:
    def __init__(self,obj,period=5,bars_len=2,isusatime=False,delta=1):
        self.obj=obj
        self.period=period
        self.iscall = True
        self.delta=delta

        self.isusatime=isusatime
        self.bar={}
        self.bars = []
        self.bars_len=bars_len
        self.onbarcall = []
        self.tas = []

    def add_ta(self, ta_arg):
        ta0 = ta_frame(ta_arg)
        self.tas.append(ta0)

    def __calc_ta(self, bar):
        for i in self.tas:
            r = i.onbar(bar)
            bar.update(r)
        return bar
    def setonbar(self,call):
        self.onbarcall.append(call)
    def onbar(self,t):
        if "uptime" in t.keys():
            timekey0=t["uptime"]
            timekey=self.getbartimekey(timekey0)
            if self.bars:
                bar0=self.bars[-1]
                if timekey==bar0["timekey"]:
                    if bar0["high"]<t["high"]:
                        bar0["high"]=t["high"]
                        bar0["hightime"] = t["hightime"]
                    elif bar0["low"] > t["low"]:
                        bar0["low"] = t["low"]
                        bar0["lowtime"] = t["lowtime"]
                    bar0["close"]=t["close"]
                    bar0["volume"]=bar0["volume"]+t["volume"]
                    bar0["vwap"]=t["vwap"]
                    bar0["pos"]=t["pos"]
                    bar0["uptime"]=timekey0
                    self.__calc_ta(bar0)
                    if self.iscall and self.onbarcall:
                        if self.isendbar(timekey0,timekey):
                            for i in self.onbarcall:
                                i(self.bar)
                            self.iscall = False
                    else:
                        self.iscall = True
                else:
                    bar0={}
                    bar0["timekey"]=timekey
                    bar0["obj"]=self.obj
                    bar0["open"]=t["open"]
                    bar0["high"]=t["high"]
                    bar0["low"]=t["low"]
                    bar0["close"]=t["close"]
                    bar0["volume"]=t["volume"]
                    bar0["vwap"]=t["vwap"]
                    bar0["pos"]=t["pos"]
                    bar0["uptime"]=timekey0
                    bar0["hightime"] = t["hightime"]
                    bar0["lowtime"] = t["lowtime"]
                    bar0["opentime"] = t["opentime"]
                    bar0=self.__calc_ta(bar0)
                    self.bars.append(bar0)
                    if len(self.bars)>self.bars_len:
                        self.bars.pop(0)
                    self.bar=bar0
                return bar0
            else:
                bar0={}
                bar0["timekey"]=timekey
                bar0["obj"]=self.obj
                bar0["open"]=t["open"]
                bar0["high"]=t["high"]
                bar0["low"]=t["low"]
                bar0["close"]=t["close"]
                bar0["volume"]=t["volume"]
                bar0["vwap"]=t["vwap"]
                bar0["pos"]=t["pos"]
                bar0["uptime"]=timekey0
                bar0["hightime"] = t["hightime"]
                bar0["lowtime"] = t["lowtime"]
                bar0["opentime"] = t["opentime"]
                self.__calc_ta(bar0)
                self.bars.append(bar0)
                self.bar=bar0
                return bar0
        else:
            timekey0 = t["timekey"]
            timekey=self.getbartimekey(timekey0)
            if self.bars:
                bar0=self.bars[-1]
                if timekey==bar0["timekey"]:
                    if bar0["high"]<t["high"]:
                        bar0["high"]=t["high"]
                    elif bar0["low"] > t["low"]:
                        bar0["low"] = t["low"]
                    bar0["close"]=t["close"]
                    bar0["volume"]=bar0["volume"]+t["volume"]
                    bar0=self.__calc_ta(bar0)
                    if self.iscall and self.onbarcall:
                        if self.isendbar(timekey0,timekey):
                            for i in self.onbarcall:
                                i(self.bar)
                            self.iscall = False
                    else:
                        self.iscall = True
                else:
                    bar0={}
                    bar0["timekey"]=timekey
                    bar0["obj"]=self.obj
                    bar0["open"]=t["open"]
                    bar0["high"]=t["high"]
                    bar0["low"]=t["low"]
                    bar0["close"]=t["close"]
                    bar0["volume"]=t["volume"]
                    bar0=self.__calc_ta(bar0)
                    self.bars.append(bar0)
                    self.bar=bar0
                return bar0
            else:
                bar0={}
                bar0["timekey"]=timekey
                bar0["obj"]=self.obj
                bar0["open"]=t["open"]
                bar0["high"]=t["high"]
                bar0["low"]=t["low"]
                bar0["close"]=t["close"]
                bar0["volume"]=t["volume"]
                bar0=self.__calc_ta(bar0)
                self.bars.append(bar0)
                self.bar=bar0
                return bar0
    def isendbar(self,a,barkey=""):
        b=a++datetime.timedelta(minutes=self.delta)
        test = self.getbartimekey(b)
        if barkey=="":
            barkey=self.getbartimekey(a)
        return test > barkey
    def getbartimekey(self,a):
        if self.isusatime:
            a = usatime2beijing(a)
        hour=a.hour
        if hour>=6:
            t0=datetime.datetime(a.year,a.month,a.day,6)
        else:
            t0 = datetime.datetime(a.year, a.month, a.day, 6)-datetime.timedelta(days=1)
        t1=a-t0
        mt=int(t1.total_seconds()/60/self.period)*self.period
        return t0+datetime.timedelta(minutes=mt)

if __name__=="__main__":
    #import pandas as pd
    #a=test_klineset()
    from jili.core import load

    t = load(r"J:\tickpkl\a1709\a1709_20160317.pkl")
    from jili.price import kline

    k = kline.kline1m("a1709", 100 * 200 * 360)
    for i in t:
        k.ontick(i)

    import pandas as pd
    kd = pd.DataFrame(k.bars)
    t = load(r"J:\tickpkl\a1709\a1709_20160318.pkl")
    for i in t:
        k.ontick(i)
    kd = pd.DataFrame(k.bars)
    k30 = kline.klinenm("a1709", 30, 100 * 200 * 360)
    for i in k.bars:
        k30.onbar(i)
    k3 = kline.klinenm("a1709", 3, 100 * 200 * 360)
    for i in k.bars:
        k3.onbar(i)
    k60 = kline.klinenm("a1709", 60, 100 * 200 * 360)
    for i in k.bars:
        k60.onbar(i)
    kd3 = pd.DataFrame(k3.bars)
    kd30 = pd.DataFrame(k30.bars)
    kd60 = pd.DataFrame(k60.bars)
